package ae.pcfc.etks.online.web.util;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;

import ae.pcfc.etks.online.web.ViewUtils;

/**
 * @author Raif.Mahmoud
 * This class will acts as a general error handling framework
 * The Class will intercept any exception generated "within" the controller and forward it to the relevant error page
 * The controller method should satisfy the following;
 * 1- Controller class within(ae.pcfc.etks.online.web..* package
 * 2- Controller method (annotated with org.springframework.web.bind.annotation.RequestMapping)
 * 3- The "last argument" in the argument list is "BindingResult errors"
 * 
 */
/**
 * @author Mohtisham.Salahria
 * 
 */
@Aspect
public class UtilityInterceptor {
	private static Logger log = null;

	public UtilityInterceptor() {
	}

	/**
	 * A join point is in the web layer if the method is defined in a type in
	 * the ae.pcfc.etks.online.web package or any sub-package under that.
	 */
	@Pointcut("within(ae.pcfc.etks.online.web..*)")
	public void inWebLayer() {
	}

	@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
	public void webControllerRequestMapper() {
	}

	@Pointcut("args(..,errors)")
	public void webControllerBindingResult(BindingResult errors) {
	}

	/**
	 * This advice will intercept any exception generated "within" the
	 * controller, and render the ViewUtils.DEFAULT_VIEW which is NULL(If a controller
	 * returns a null view name, or declares a void return type, Spring will
	 * attempt to infer the view name from the request URL.In this case 
	 * it will be the same view name as @RequestMapping("/{viewName}"), 
	 * so it will mainly return to same view)
	 * The controller request handler method should satisfy the following; 
	 * 1- Controller class within(ae.pcfc.etks.online.web..* package 
	 * 2- Controller method (annotated with org.springframework.web.bind.annotation.RequestMapping) 
	 * 3- The "last argument" in the argument list is "BindingResult errors"
	 */
	@Around("inWebLayer() && webControllerRequestMapper() && webControllerBindingResult(errors)")
	public Object handleException(ProceedingJoinPoint pjp, BindingResult errors)
			throws Throwable {
		log = LoggerFactory.getLogger(pjp.getTarget().getClass());
		Object targetPage = ViewUtils.DEFAULT_VIEW;
		try {
			targetPage = pjp.proceed();
		} catch (Exception exception) {
			log.error("AOP handler Caught Exception : ", exception);
			ViewUtils.addExceptionError(exception, errors);
		}
		return targetPage;

	}

	@AfterThrowing(pointcut = "inWebLayer()", throwing = "ex")
	public void logException(JoinPoint joinPoint, Exception ex) {
		log = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
		log.error("Error", ex);
	}

	@Before("inWebLayer()")
	public void logBeforeInfo(JoinPoint joinPoint) throws Throwable {

		log = LoggerFactory.getLogger(joinPoint.getTarget().getClass());

		if (log.isInfoEnabled()) {
			Signature sig = joinPoint.getSignature();

			Object[] args = joinPoint.getArgs();
			String[] names = ((CodeSignature) sig).getParameterNames();
			Class[] types = ((CodeSignature) sig).getParameterTypes();
			StringBuffer logMessageStringBuffer = new StringBuffer();

			for (int i = 0; i < args.length; i++) {
				logMessageStringBuffer.append("[Name=" + names[i] + " , Type="
						+ types[i].getName() + " , Value= " + args[i] + "]");

			}
			log.info("Entering method: {} , Argument values: {}",
					sig.getName(), logMessageStringBuffer.toString());
		}

	}

	@AfterReturning("inWebLayer()")
	public void logAfterInfo(JoinPoint joinPoint) throws Throwable {

		if (log.isInfoEnabled()) {
			log = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
			Signature sig = joinPoint.getSignature();
			log.info("Successfuly Leaving method: {} ", sig.getName());
		}

	}

}
